package br.eti.robsonfranca.clf4j.impl.win;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import br.eti.robsonfranca.clf4j.enums.ClearOp;
import br.eti.robsonfranca.clf4j.impl.Color;
import br.eti.robsonfranca.clf4j.interfaces.Console;
import br.eti.robsonfranca.clf4j.interfaces.win.Kernel32;

import com.google.inject.Singleton;
import com.sun.jna.Pointer;
import com.sun.jna.ptr.IntByReference;

@Singleton
public class WinConsoleImpl implements Console {

	private static final int STD_OUTPUT_HANDLE = -11;
	private static final int STD_INPUT_HANDLE = -10;
	
	private static final int WIDTH = 80;
	private static final int HEIGHT = 25;
	
	private Coord savedPos;
	private Pointer outHandle;
	private Pointer inHandle;
	private Kernel32 k;
	
	
	@Override
	public void clrLine(ClearOp clo) {		

	}

	@Override
	public void clrscr(ClearOp clo) {
		if (clo == ClearOp.CURSOR_WHOLELINE) {
			Coord.ByValue c = new Coord.ByValue();
			c.x = 0;
			c.y = 0;
			StringBuilder sb = new StringBuilder();
			for(int i=0;i<getWidth()*getHeight();i++) {
				sb.append(' ');
			}
			String text = sb.toString();
			gotoxy(0,0);
			k.WriteConsoleW(outHandle, text.toCharArray(), text.length(), new IntByReference(), Pointer.NULL);
			gotoxy(0,0);
		}
	}

	@Override
	public int getHeight() {
		return HEIGHT;
	}

	@Override
	public int getWidth() {
		return WIDTH;
	}

	@Override
	public void gotoxy(int x, int y) {
		Coord.ByValue c = new Coord.ByValue();
		c.x = (short) x;
		c.y = (short) y;
		k.SetConsoleCursorPosition(outHandle, c);
	}

	@Override
	public void moveDown(int n) {


	}

	@Override
	public void moveLeft(int n) {


	}

	@Override
	public void moveNextLine(int n) {

	}

	@Override
	public void movePrevLine(int n) {

	}

	@Override
	public void moveRight(int n) {

	}

	@Override
	public void moveUp(int n) {

	}

	@Override
	public void printf(String format, Object... args) {
		String text = String.format(format, args);
		IntByReference i = new IntByReference();
		k.WriteConsoleW(outHandle, text.toCharArray(), text.length(), i, Pointer.NULL);
	}

	@Override
	public void restCurPos() {
		gotoxy(savedPos.x, savedPos.y);
	}

	@Override
	public void saveCurPos() {
		ConsoleScreenBufferInfo csbi = new ConsoleScreenBufferInfo();
		k.GetConsoleScreenBufferInfo(outHandle, csbi);
		savedPos.x = csbi.dwCursorPosition.x;
		savedPos.y = csbi.dwCursorPosition.y;
	}

	@Override
	public String[] scanf(String format) {
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
		String linha;
		String[] ret = null;
		try {
			linha = br.readLine();
			Pattern p = Pattern.compile(format);
			Matcher m = p.matcher(linha);
			if (m.find()) {
				int c = m.groupCount();
				ret = new String[c];
				for(int i=1;i<=c;i++) {
					ret[i-1] = m.group(i); 
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
			ret = null;
		}
		return ret;
	}

	@Override
	public void scrollDown(int n) {

	}

	@Override
	public void scrollUp(int n) {

	}

	@Override
	public void sendANSI(String command) {
		// Not implemented!
	}

	@Override
	public void setBackColor(Color color) {
		ConsoleScreenBufferInfo csbi = new ConsoleScreenBufferInfo();
		k.GetConsoleScreenBufferInfo(outHandle, csbi);
		short cor = (short) (csbi.wAttributes & 0x0F);
		cor |= (WinColor.toWinColor(color) << 4);
		k.SetConsoleTextAttribute(outHandle, cor);		
	}

	@Override
	public void setForeColor(Color color) {
		ConsoleScreenBufferInfo csbi = new ConsoleScreenBufferInfo();
		k.GetConsoleScreenBufferInfo(outHandle, csbi);
		short cor = (short) (csbi.wAttributes & 0xF0);
		cor |= WinColor.toWinColor(color);
		k.SetConsoleTextAttribute(outHandle, cor);
	}

	public WinConsoleImpl() {
		this.k = Kernel32.INSTANCE;
		this.inHandle = k.GetStdHandle(STD_INPUT_HANDLE);
		this.outHandle = k.GetStdHandle(STD_OUTPUT_HANDLE);
		this.savedPos = new Coord();
		SmallRect console = new SmallRect();
		console.left = 0;
		console.top = 0;
		console.right = (short) (getWidth() - 1);
		console.bottom = (short) (getHeight() - 1);
		k.SetConsoleWindowInfo(outHandle, true, console);
	}

	@Override
	public void hideCursor() {
		ConsoleCursorInfo c = new ConsoleCursorInfo();
		k.GetConsoleCursorInfo(outHandle, c);
		c.visible = false;
		k.SetConsoleCursorInfo(outHandle, c);
		
	}

	@Override
	public void showCursor() {
		ConsoleCursorInfo c = new ConsoleCursorInfo();
		k.GetConsoleCursorInfo(outHandle, c);
		c.visible = true;
		k.SetConsoleCursorInfo(outHandle, c);		
	}
	
	@Override
	public char getChar() {
		char[] listaCarac = new char[1];
		IntByReference nocr = new IntByReference();
		k.ReadConsoleW(inHandle, listaCarac, listaCarac.length, nocr, Pointer.NULL);
		return listaCarac[0];
	}
}
